package parser

import (
	"fmt"
	"strings"

	"gitee.com/zfd81/dbrs/sql"

	"github.com/zfd81/rooster/util"

	"gitee.com/zfd81/dbrs/expr"

	"github.com/spf13/cast"
)

const (
	Sys_Variable_Prefix  = "_sys"
	User_Variable_Prefix = "_user"
)

type Statement interface {
	Type() string
	SQL() string
	Node() sql.Node
}

func (*InsertStatement) Type() string {
	return "Insert"
}

func (*DeleteStatement) Type() string {
	return "Delete"
}

func (*UpdateStatement) Type() string {
	return "Update"
}

func (*SelectStatement) Type() string {
	return "Select"
}

func (*ShowStatement) Type() string {
	return "Show"
}

func (*SetStatement) Type() string {
	return "Set"
}

func (*UseStatement) Type() string {
	return "Use"
}

func (*DescStatement) Type() string {
	return "desc"
}

func (*AlterTableStatement) Type() string {
	return "AlterTable"
}

func (*DropTableStatement) Type() string {
	return "DropTable"
}

type BaseStatement struct {
	sql  string
	node sql.Node
}

func (s *BaseStatement) SQL() string {
	return s.sql
}

func (s *BaseStatement) Node() sql.Node {
	return s.node
}

type FieldType uint32

const (
	FieldType_SystemVariable FieldType = iota
	FieldType_UserVariable
	FieldType_DataColumn
	FieldType_Function
	FieldType_BinaryExpr
	FieldType_ComparisonExpr
)

type Field struct {
	name string
	sql  string
	expr string
}

func (f *Field) Name() string {
	return f.name
}

func (f *Field) SetExpr(str string) {
	expr := strings.ReplaceAll(str, "@@", Sys_Variable_Prefix+".")
	expr = strings.ReplaceAll(expr, "@", User_Variable_Prefix+".")
	f.sql = str
	f.expr = strings.ReplaceAll(expr, " = ", " == ")
}

func (f *Field) Expr() string {
	return f.expr
}

type Column struct {
	name   string
	typ    FieldType
	values []interface{}
	expr   string
	alias  string
	db     string
	tbl    string
}

func (c *Column) Name() string {
	return c.name
}

func (c *Column) Type() FieldType {
	return c.typ
}

func (c *Column) Values() []interface{} {
	return c.values
}

func (c *Column) AddValue(vals ...interface{}) {
	c.values = append(c.values, vals...)
}

func (c *Column) SetValue(vals ...interface{}) {
	c.values = vals
}

func (c *Column) GetExpr() string {
	return c.expr
}

func (c *Column) Alias() string {
	return c.alias
}

func (c *Column) Database() string {
	return c.db
}
func (c *Column) Table() string {
	return c.tbl
}

func NewColumn(expr, name, alias string) *Column {
	col := &Column{
		expr:  expr,
		alias: alias,
	}
	col.name = expr[strings.LastIndex(name, ".")+1:]
	if alias == "" {
		col.name = expr
	}
	return col
}

type ColumnSet map[string]*Column

func (s ColumnSet) Add(col *Column) {
	_, ok := s[col.expr]
	if !ok {
		s[col.expr] = col
	}
}

func (s ColumnSet) Get(name string) *Column {
	return s[name]
}

func (s ColumnSet) Names() []string {
	names := make([]string, len(s))
	index := 0
	for k, _ := range s {
		names[index] = k
		index++
	}
	return names
}

func (s ColumnSet) Keyvalues(key string) []string {
	col, ok := s[key]
	if ok {
		keys := make([]string, len(col.Values()))
		for i, val := range col.Values() {
			keys[i] = cast.ToString(val)
		}
		return keys
	}
	return nil
}

type TableItem struct {
	Qualifier string //限定符
	Name      string //表名
	Expr      string
	As        string
}

type Condition struct {
	Left     string
	Operator string
	Right    []byte
}

type WhereExpr struct {
	sql  string
	expr string
}

func (w *WhereExpr) setExpr(str string) error {
	sql, err := util.ReplaceBetween(str, "#[", "]#", func(index int, start int, end int, content string) (string, error) {
		return "(" + content + ")", nil
	})
	if err != nil {
		return err
	}
	w.sql = sql

	expr, err := util.ReplaceBetween(str, "#[", "]#", func(index int, start int, end int, content string) (string, error) {
		return "[" + content + "]", nil
	})
	if err != nil {
		return err
	}
	w.expr = strings.ReplaceAll(expr, " = ", " == ")
	return nil
}

func (w *WhereExpr) Eval(data map[string]interface{}) (bool, error) {
	v, err := expr.Eval(w.expr, data)
	if err != nil {
		return false, err
	}
	is, ok := v.(bool)
	if ok {
		return is, nil
	}
	return false, fmt.Errorf("syntax error: near %", w.sql)
}

func NewWhereExpr() *WhereExpr {
	return &WhereExpr{}
}
